/*
 * SlidingWindow.java
 *
 * Created by Sheng-Yao Tseng 2011-04-29
 *
 * Copyright (c) 2011-04-29
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the project's author nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package tw.qlife.atobe;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import javax.swing.JPanel;

public class BoxPanel extends JPanel implements Runnable {

	private static final long serialVersionUID = 1L;
	private int m_BoxPixel = 50; // A box is 50px X 50px
	private Point m_Viewing = null;
	private int m_StepPixel = 20;
	public Thread thread = null;
	private BufferedImage m_Image = null;
	private volatile Point m_LastMousePosition = null;
	private volatile boolean isRunning = false;

	public BoxPanel(int BoxPixel, int StepPixel) {
		m_Viewing = new Point(0, 0);
		m_BoxPixel = BoxPixel;
		m_StepPixel = StepPixel;
		m_Image = null;
		setBackground(Color.BLACK);
	}

	@Override
	public Dimension getMaximumSize() {
		return new Dimension(99999, 99999);
	}

	@Override
	public Dimension getMinimumSize() {
		return new Dimension(3 * m_BoxPixel + 2 * m_StepPixel, 3 * m_BoxPixel
				+ 2 * m_StepPixel);
	}

	@Override
	public Dimension getPreferredSize() {
		return new Dimension(16 * m_BoxPixel, 10 * m_BoxPixel);
	}

	public boolean isGoingUpward(Point mousePosition) {
		Rectangle movingRegion = new Rectangle(0, 0, getWidth(), m_BoxPixel);
		return movingRegion.contains(mousePosition);
	}

	public boolean isGoingDownward(Point mousePosition) {
		Rectangle movingRegion = new Rectangle(0, getHeight() - m_BoxPixel,
				getWidth(), m_BoxPixel);
		return movingRegion.contains(mousePosition);
	}

	public boolean isGoingLeftSide(Point mousePosition) {
		Rectangle movingRegion = new Rectangle(0, 0, m_BoxPixel, getHeight());
		return movingRegion.contains(mousePosition);
	}

	public boolean isGoingRightSide(Point mousePosition) {
		Rectangle movingRegion = new Rectangle(getWidth() - m_BoxPixel, 0,
				m_BoxPixel, getHeight());
		return movingRegion.contains(mousePosition);
	}

	public int getBoxPixel(int pixel) {
		return m_BoxPixel;
	}

	public void setBoxPixel(int pixel) {
		if (1 < pixel)
			m_BoxPixel = pixel;
		repaint();
	}

	public void DetermineScrolling() {
		boolean up, down, left, right;
		up = isGoingUpward(m_LastMousePosition);
		down = isGoingDownward(m_LastMousePosition);
		left = isGoingLeftSide(m_LastMousePosition);
		right = isGoingRightSide(m_LastMousePosition);
		if (up) {
			m_Viewing.x -= m_StepPixel;
		}

		if (down) {
			m_Viewing.x += m_StepPixel;
		}

		if (left) {
			m_Viewing.y -= m_StepPixel;
		}

		if (right) {
			m_Viewing.y += m_StepPixel;
		}

		if (!up && !down && !left && !right) {
			stop();
		} else {
			if (!isRunning) {
				start();
			}
		}
	}

	@Override
	protected void processMouseMotionEvent(MouseEvent e) {
		m_LastMousePosition = e.getPoint();
		
		DetermineScrolling();
	}

	public static Color getModuloColor(int value) {
		int reminder = value % 13;
		Color result = null;
		switch (reminder) {
		case 0:
			result = Color.BLACK;
			break;
		case 1:
			result = Color.BLUE;
			break;
		case 2:
			result = Color.CYAN;
			break;
		case 3:
			result = Color.DARK_GRAY;
			break;
		case 4:
			result = Color.GRAY;
			break;
		case 5:
			result = Color.GREEN;
			break;
		case 6:
			result = Color.LIGHT_GRAY;
			break;
		case 7:
			result = Color.MAGENTA;
			break;
		case 8:
			result = Color.ORANGE;
			break;
		case 9:
			result = Color.PINK;
			break;
		case 10:
			result = Color.RED;
			break;
		case 11:
			result = Color.WHITE;
			break;
		case 12:
			result = Color.YELLOW;
			break;
		}
		return result;
	}

	public void start() {

		thread = new Thread(this);
		thread.setPriority(Thread.MIN_PRIORITY);
		thread.setName("BoxPanel");

	}

	public synchronized void stop() {
		thread = null;
		isRunning = false;
		notify();
	}

	/**
	 * Per-compute the next frame image into a private image buffer, which is
	 * determined by the coordinate of up-left corner.
	 * 
	 * @param pos
	 *            The given coordinate.
	 */

	private void drawNextFrame(Point pos) {
		m_Image = new BufferedImage(getWidth(), getHeight(),
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g2 = m_Image.createGraphics();
		g2.setColor(Color.RED);
		g2.fillRect(0, 0, getWidth(), getHeight());
		int xBoxPos, yBoxPos;
		xBoxPos = pos.x / m_BoxPixel + 1;
		yBoxPos = pos.y / m_BoxPixel + 1;
		int startPos = xBoxPos + yBoxPos;

		int reminX, reminY;
		reminX = m_Viewing.x % m_BoxPixel;
		reminY = m_Viewing.y % m_BoxPixel;

		int xGrids = getWidth() / m_BoxPixel;
		int yGrids = getHeight() / m_BoxPixel;

		if (reminY > m_StepPixel) {
			Rectangle rectReminHorizontial = new Rectangle(0, 0, m_BoxPixel,
					reminY);
			for (int i = 0; i < xGrids; ++i) {
				g2.setColor(BoxPanel.getModuloColor(i + startPos));
				g2.fill(rectReminHorizontial);
				rectReminHorizontial.setLocation(i * m_BoxPixel, 0);
			}
		}

		if (reminX > m_StepPixel) {
			Rectangle rectReminVertical = new Rectangle(0, 0, reminX,
					m_BoxPixel);
			for (int j = 0; j < xGrids; ++j) {
				g2.setColor(BoxPanel.getModuloColor(j + startPos));
				g2.fill(rectReminVertical);
				rectReminVertical.setLocation(0, j * m_BoxPixel);
			}
		}

		Rectangle box = new Rectangle(reminX, reminY, m_BoxPixel, m_BoxPixel);
		int boxPos = startPos + 2;
		for (int j = 0; j < yGrids; ++j) {
			for (int i = 0; i < xGrids; ++i) {
				g2.setColor(BoxPanel.getModuloColor(boxPos + i + j));
				g2.fill(box);
				box.setLocation(reminX + i * m_BoxPixel, reminY + j
						* m_BoxPixel);
			}
		}
	}

	@Override
	public void paintComponent(Graphics g) {
		drawNextFrame(m_Viewing);
		g.drawImage(m_Image, 0, 0, this);
	}

	@Override
	public void run() {

		Thread me = Thread.currentThread();

		if (me == thread && !isShowing() || getSize().height == 0) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				isRunning = false;
				return;
			}
		}

		if (me == thread && isShowing()) {
			DetermineScrolling();
		}
	}

}
